import sys
from pprint import pprint

from pydantic import BaseModel
from typing import List, Dict

from nornir import InitNornir
from nornir_netmiko import netmiko_send_command
from nornir_utils.plugins.functions import print_result

nr = InitNornir(config_file='config.yaml')

result_neigh = nr.run(netmiko_send_command, command_string="show lldp neighbors detail", use_textfsm=True)

if result_neigh.failed_hosts:
    print('-'*111)
    print(f'failed to connect following servers : {result_neigh.failed_hosts}')
    sys.exit(1)

result_name = nr.run(netmiko_send_command, command_string="show hostname", use_textfsm=True)
# result = nr.run(netmiko_send_command, command_string="show lldp neighbors ", use_textfsm=True)
# result = nr.run(netmiko_send_command, command_string="show ip interface brief", use_textfsm=True)
# result = nr.run(netmiko_send_command, command_string="show interface status", use_textfsm=True)
# result = nr.run(netmiko_send_command, command_string="show ip interface brief ", use_textfsm=True)
# print_result(result)

# print(result['vEOS03'].result)
# r = result['vEOS03'].result
# n = result['nxos-04'].result


class Host(BaseModel):
    id: int = "0"
    name: str = ""
    system_id: str = ""
    mgmt_address: str = ""
    conn_address: str = ""
    # platform_version: str = ""
    system_description: str = ""
    local_ports: Dict[str, Dict] = {}
    remote_ports: Dict[str, str] = {}
    # remote_ports: List[str] = []
    capabilities: List[str] = ['R']
    # neighbors: List[str] = []
    neighbors: Dict[str, Dict] = {}
    neighbor_number: int = 0

    # newid = itertools.count().next
    # @property
    # def id(self) -> int:
    #     return abs(hash(self.name))

    # def __init__(self):
    #     super().__init__()
    #     self.id = abs(hash(self.name))
    # self.id = Host.newid()

# @todo unify interface name


hostname_id_map = []  # [{system_id : hostname}, ]
systemID_connIP_map = {}
# hosts = {}
hosts = []  # [Host(), Host(), ]
links = []  # [{host: host}, ]
# @tag links_id = []  # [{sys_id, sys_id}, ]
links_set = set()  # {host01+port01_host02+port02, host02+port02_host01+port01}

for host in nr.inventory.hosts:
    neighbors = result_neigh[host].result
    target = Host()
    target.conn_address = nr.inventory.hosts[host].hostname
    target.neighbor_number = len(neighbors)
    target.name = result_name[host].result[0]['hostname']
    target.id = str(abs(hash(target.name)))
    if nr.inventory.hosts[host].platform == 'eos':
        for neighbor in neighbors:
            target.neighbors.update({neighbor['dest_host']: neighbor})
            target.local_ports.update({neighbor['local_port']: {neighbor['dest_host']: neighbor['remote_port']}})
            target.remote_ports.update({neighbor['remote_port']: neighbor['local_port']})

            # hosts.append({neighbor['dest_host']: neighbor['system_id']})
            if neighbor['system_id'] not in [i[1] for i in hostname_id_map]:
                hostname_id_map.append([neighbor['dest_host'], neighbor['system_id'], ])

            # manupulate links
            combine01 = '+'.join([target.name, neighbor['local_port'], neighbor['dest_host'], neighbor['remote_port']])
            combine02 = '+'.join([neighbor['dest_host'], neighbor['remote_port'], target.name, neighbor['local_port']])
            if combine01 not in links_set and combine02 not in links_set:
                links.append([target.name, neighbor['dest_host']])
            links_set.add(combine01)
            links_set.add(combine02)
            # print(result[host].result)
            # sys.exit(0)
    elif nr.inventory.hosts[host].platform == 'cisco_nxos':
        pass
    else:
        pass

    hosts.append(target)
    # print('-'*111)
    # print(target.dict())

# print(hosts)
for target in hosts:
    try:
        if [i[0] for i in hostname_id_map].count(target.name) == 1:
            target.system_id = [i[1] for i in hostname_id_map][[i[0] for i in hostname_id_map].index(target.name)]
    except ValueError:
        pass
    # @mark
    # if
    # links.append({})
    # print('-'*111)
    # print(target.dict())


# @todo number of found neighbors may not equal to host number
#   @done discovered host was put into hosts list with  'name' 'id'
for name, sys_id in hostname_id_map:
    if name not in [i.name for i in hosts]:
        hosts.append(Host(id=str(abs(hash(sys_id))), name=name, system_id=sys_id))


# generate nodes & edges for frontend
nodes = []
edges = []

for host in hosts:
    nodes.append({'id': host.name, 'label': host.name})

for link in links:
    edges.append({'source': link[0], 'target': link[1]})

print('-'*111)
print(hostname_id_map)
# print('-'*111)
# pprint(hosts)
# pprint(links_set)
pprint(links)
print('-'*111)
print('nodes = ', end='')
pprint(nodes)
print('edges = ', end='')
pprint(edges)
